﻿using System.Collections.Generic;
using LightPersist.Exceptions;
using LightPersist.Metainfo;
using System.Collections;

namespace LightPersist
{
	internal class NavigationConsistencyListener : IModelChangingListener
	{
		readonly LightPersistContext _lightPersistContext;

		public NavigationConsistencyListener(LightPersistContext context)
		{
			_lightPersistContext = context;
		}

		public void OnListRemoved(ILightList lightList, int index, ModelBase itemToRemove)
		{
		}

		public void OnListRemoving(ILightList lightList, int index, ModelBase itemToRemove)
		{
			var pi = SearchForInverseOfInCollectionElement(lightList, itemToRemove);

			if (pi != null)
			{
				var list = itemToRemove.GetList(pi.Name);

				var cnt = list.Count;
				if (!pi.IsList)
				{
					if (cnt > 1)
					{
						throw new LightPersistException(
							"Removing object from collectin, which is InverseOf other reference proeprty: property does not keep one value, it keeps " + cnt);
					}
				}

				// do not use list.Add to skip r/o checking
				if (cnt > 0)
				{
					_lightPersistContext.ListRemoveAt(list, list.IndexOf(lightList.Owner), lightList.Owner);
				}
			}
		}

		public void OnListInserted(ILightList lightList, int index, ModelBase itemToInsert)
		{
			var pi = SearchForInverseOfInCollectionElement(lightList, itemToInsert);

			if (pi != null)
			{
				var list = itemToInsert.GetList(pi.Name);

				if (!pi.IsList)
				{
					var cnt = list.Count;
					if (cnt != 0)
					{
						throw new LightPersistException(
							"Inserting object with InverseOf defined: this object already referenced by {0} elements".Arg(cnt));
					}
				}

				// do not use list.Add to skip r/o checking
				_lightPersistContext.ListInsert(list, list.Count, lightList.Owner);
			}
		}

		private static PropertyMetainfo SearchForInverseOfInCollectionElement(ILightList lightList, ModelBase itemToInsert)
		{
			// todo optimize PropertiesAll
			foreach (var pi in itemToInsert.ObjectState.ClassMetainfo.PropertiesAll)
			{
				if (pi.IsInverseOf)
				{
					var insertingInto = lightList.PropertyName;
					if (insertingInto == pi.InverseOf && pi.TypeClr.IsAssignableFrom(lightList.Owner.GetType()))
					{
						return pi;
					}
				}
			}
			return null;
		}

		public void OnListInserting(ILightList lightList, int index, ModelBase itemToInsert)
		{

		}

		public void OnPrimitivePropertyChanged(ModelBase item, string property, object value)
		{

		}
	}
}